<html>
    <head>
        <title>高级主题</title>
    </head>
    <body>
        <script>
            /*
                     九、高级主题

                                1、控制多租户

                                                如果你的解决方案是多租户，租户可能有单独的数据库，你的解决方案中有多个DbContext类，
                                                
                                                并且你的某些DbContext类应该是主机端通用的，不是针对某个租户，而是所有租户都通用的，

                                                建议在你的DbContext类上添加[IgnoreMultiTenancy]属性。

                                                在这种情况下，ABP保证相关的DbContext始终使用主机连接字符串，即使你在租户上下文中。

                                                eg：

                                                    [IgnoreMultiTenancy]  // 忽略多租户，说明该DbContext不区分租户，始终使用主机连接字符串。
                                                    public class MyDbContext : AbpDbContext<MyDbContext>
                                                    {
                                                        ...
                                                    }

                                                ★、如果DbContext中的任何一个实体可以持久化在租户数据库中，请不要使用[IgnoreMultiTenancy]属性。

                                            备注：

                                                当你使用存储库时，ABP已经使用了主机数据库，因为实体没有实现IMultiTenant接口。

                                                因此，如果你使用存储库来处理数据库，大多数时候你不需要[IgnoreMultiTenancy]属性。

                                2、设置默认存储库

                                           默认情况下，默认通用存储库由EfCoreRepository类实现。你可以创建自己的实现并将其用于所有默认存储库实现。
                                           
                                           首先，像这样定义你的默认存储库类：

                                                    // 用于复合主键实体
                                                    public class MyRepositoryBase<TEntity>
                                                        : EfCoreRepository<BookStoreDbContext, TEntity>
                                                        where TEntity : class, IEntity  // 泛型约束
                                                    {
                                                        public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
                                                            : base(dbContextProvider)
                                                        {
                                                        }
                                                    }

                                                    // 用于单个主键的实体
                                                    public class MyRepositoryBase<TEntity, TKey>
                                                        : EfCoreRepository<BookStoreDbContext, TEntity, TKey>
                                                        where TEntity : class, IEntity<TKey> // 反应约束
                                                    {
                                                        public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
                                                            : base(dbContextProvider)
                                                        {
                                                        }
                                                    }

                                                    第一个用于具有复合主键的实体，第二个用于具有单个主键的实体。

                                            建议从EfCoreRepository类继承并在需要时覆盖方法。否则，你将不得不手动实现所有标准存储库方法。

                                            现在，你可以使用SetDefaultRepositoryClasses选项：

                                                    context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
                                                    {
                                                        options.SetDefaultRepositoryClasses(  // 设置默认存储库的实现类
                                                            typeof(MyRepositoryBase<,>),
                                                            typeof(MyRepositoryBase<>)
                                                        );

                                                        //...
                                                    });
                                        
                                3、为默认存储库设置基本DbContext类或接口

                                            果你的DbContext继承自另一个DbContext或实现了一个接口，你可以将该基类或接口用作默认存储库的DbContext。
                                            
                                            示例：

                                                public interface IBookStoreDbContext : IEfCoreDbContext
                                                {
                                                    DbSet<Book> Books { get; }
                                                }
                                            IBookStoreDbContext由BookStoreDbContext类实现。然后你可以使用AddDefaultRepositories的通用重载：

                                                context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
                                                {
                                                    options.AddDefaultRepositories<IBookStoreDbContext>();
                                                    //...
                                                });

                                            现在，你的自定义BookRepository也可以使用IBookStoreDbContext接口：

                                                public class BookRepository : EfCoreRepository<IBookStoreDbContext, Book, Guid>, IBookRepository
                                                {
                                                    //...
                                                }

                                            将接口用于DbContext的一个优点是它可以被另一个实现替换。

                                
                                4、替换其他DbContextes

                                            一旦你正确定义和使用了DbContext的接口，那么任何其他实现都可以使用以下方式来替换它：

                                            ①、 [ReplaceDbContext ]特性，替换 DbContext

                                                        [ReplaceDbContext(typeof(IBookStoreDbContext))]  // 替换DbContext
                                                        public class OtherDbContext : AbpDbContext<OtherDbContext>, IBookStoreDbContext
                                                        {
                                                            //...
                                                        }

                                            ②、ReplaceDbContext 方法替换默认的 DbContext

                                                        context.Services.AddAbpDbContext<OtherDbContext>(options =>
                                                        {
                                                            //...
                                                            options.ReplaceDbContext<IBookStoreDbContext>();
                                                        });

                                                        在此示例中，OtherDbContext实现IBookStoreDbContext。

                                                        此功能允许你在开发时拥有多个DbContext（每个模块一个），但在运行时拥有单个DbContext（实现所有DbContext的所有接口）。

                                            ③、用多租户替换

                                                        也可以基于多租户端替换DbContext。

                                                        ReplaceDbContext特性 和ReplaceDbContext方法可以获得默认值为MultiTenancySides的MultiTenancySides选项。

                                                    eg: 仅为租户替换DbContext，使用[ReplaceDbContext] 特性：

                                                                [ReplaceDbContext(typeof(IBookStoreDbContext), MultiTenancySides.Tenant)]

                                                    eg: 仅为主机端替换DbContext，使用ReplaceDbContext方法

                                                                options.ReplaceDbContext<IBookStoreDbContext>(MultiTenancySides.Host);


                                5、拆分查询

                                        ABP默认全局启用拆分查询以获得更好的性能。你可以根据需要进行更改。

                                        eg:
                                            Configure<AbpDbContextOptions>(options =>
                                            {
                                                options.UseSqlServer(optionsBuilder =>
                                                {
                                                    optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
                                                });
                                            });

                                            什么是拆分查询？  看EF Core Pdf


                                6、自定义批量操作

                                         如果你有更好的逻辑或使用外部库进行批量操作，你可以通过实现IEfCoreBulkOperationProvider来覆盖该逻辑。

                                         你可以使用下面的示例模板：

                                                public class MyCustomEfCoreBulkOperationProvider
                                                    : IEfCoreBulkOperationProvider, ITransientDependency
                                                {
                                                    public async Task DeleteManyAsync<TDbContext, TEntity>(
                                                        IEfCoreRepository<TEntity> repository,
                                                        IEnumerable<TEntity> entities,
                                                        bool autoSave,
                                                        CancellationToken cancellationToken)
                                                        where TDbContext : IEfCoreDbContext
                                                        where TEntity : class, IEntity
                                                    {
                                                        // Your logic here.
                                                    }

                                                    public async Task InsertManyAsync<TDbContext, TEntity>(
                                                        IEfCoreRepository<TEntity> repository,
                                                        IEnumerable<TEntity> entities,
                                                        bool autoSave,
                                                        CancellationToken cancellationToken)
                                                        where TDbContext : IEfCoreDbContext
                                                        where TEntity : class, IEntity
                                                    {
                                                        // Your logic here.
                                                    }

                                                    public async Task UpdateManyAsync<TDbContext, TEntity>(
                                                        IEfCoreRepository<TEntity> repository,
                                                        IEnumerable<TEntity> entities,
                                                        bool autoSave,
                                                        CancellationToken cancellationToken)
                                                        where TDbContext : IEfCoreDbContext
                                                        where TEntity : class, IEntity
                                                    {
                                                        // Your logic here.
                                                    }
                                                }

                */
        </script>
    </body>
</html>